package factory;

import beans.BeanDefinition;
import beans.BeanReference;
import beans.PropertyValue;
import beans.PropertyValues;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by inuyasha on 18-3-22.
 */
public class DefaultListableBeanFactory extends AbstractBeanFactory implements BeanDefinitionRegisty,ConfigurableListableBeanFactory{

    private Map<String,BeanDefinition> map = new ConcurrentHashMap<String, BeanDefinition>();
    private List<String> BeanDefinitionNames = new ArrayList<String>();

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        map.put(beanName,beanDefinition);
        BeanDefinitionNames.add(beanName);
    }

    protected BeanDefinition getBeanDefinitionByName(String beanName) {
        return map.get(beanName);
    }

    protected Object doCreateBean(BeanDefinition beanDefinition){
        Object bean = createInstance(beanDefinition);
        addProperty(bean,beanDefinition.getPropertyValues());
        return bean;
    }

    protected void addProperty(Object bean, PropertyValues propertyValues) {
        for (PropertyValue propertyValue: propertyValues.getList()) {
            String name = propertyValue.getName();
            Object value = propertyValue.getValue();
            if(value instanceof BeanReference){
                value = getBean(((BeanReference) value).getRef());
            }
            try {
                Method method = bean.getClass().getDeclaredMethod("set" + name.substring(0,1).toUpperCase() + name.substring(1),bean.getClass(),value.getClass());
                method.setAccessible(true);
                method.invoke(bean,value);
            }catch (Exception e){
                try {
                    Field field = bean.getClass().getDeclaredField(name);
                    field.setAccessible(true);
                    field.set(bean,value);
                } catch (Exception e1) {
                    throw new RuntimeException("add property " + name + " failed");
                }
            }
        }
    }

    protected Object createInstance(BeanDefinition beanDefinition){
        try {
            if(beanDefinition.getaClass() != null){
                return beanDefinition.getaClass().newInstance();
            }else if(beanDefinition.getClassName() != null){
                String className = beanDefinition.getClassName();
                Class c = Class.forName(className);
                return c.newInstance();
            }
        } catch (Exception e) {
            throw new RuntimeException("create bean " + beanDefinition.getName() + " failed");
        }
        throw new RuntimeException("bean :" + beanDefinition.getName() +" don't have class");
    }

    public void preInstantiateSingletons() throws Exception {
        for(String beanName : BeanDefinitionNames){
            getBean(beanName);
        }
    }
}
